/*  
 *  Copyright 2006 Paul Jack.
 *
 *  This file is part of the Dex compiler suite. 
 *  
 *  Dex is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 2 of the License, or (at your
 *  option) any later version.
 *  
 *  Dex is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 *  Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */
package dex.compiler.parser;

import java.util.ArrayList;
import java.util.List;

import dex.compiler.model.type.ArrayTypeNode;
import dex.compiler.model.type.BasicTypeName;
import dex.compiler.model.type.BasicTypeNode;
import dex.compiler.model.type.FunctionTypeNode;
import dex.compiler.model.type.NameCache;
import dex.compiler.model.type.TypeNode;


/**
 *  Parser for <code>TypeNode</code> objects. 
 */
class TypeParser extends ParserBase {

	
	
	final NameCache nameCache;
	
	

	/**
	 * Constructs a new TypeParser.
	 * 
	 * @param config  the configuration for the parser
	 */
	public TypeParser(ParserConfig config) {
		super(config);
		this.nameCache = new NameCache();
	}
	
	
	/**
	 * Parses a list of types.  An open parenthesis, followed by 
	 * types separated by commas, followed by a closed parenthesis.
	 * Used for function types.
	 * 
	 * @return  the type list
	 */
	protected List<TypeNode> parseTypeList() {
		input.skipAndExpect('(');
		List<TypeNode> params = new ArrayList<TypeNode>();
		char ch = input.skipAndPeek();
		if (ch != ')') {
			params.add(parseType());
			ch = input.skipAndPeek();
			while (ch == ',') {
				input.expect(',');
				params.add(parseType());
				ch = input.skipAndPeek();
			}
		}
		input.skipAndExpect(')');
		return params;
	}
	
	
	/**
	 * Parses a type tail.  A base type can be followed by any series of
	 * [] or (...) to indicate array or function types.  Eg:
	 * 
	 * <pre>
	 *   Foo     // base type
	 *   Foo[]   // array of Foo
	 *   Foo()   // Function type pointing to a function with no args, returns Foo
	 *   Foo()[] // array of Foo() function pointers
	 *   Foo[]() // function ointer that returns an array of Foo
	 * <pre>
	 * 
	 * And so on.
	 * 
	 * @return  the parsed type tail
	 */
	protected TypeNode parseTypeTail(TypeNode base) {
		char ch = input.peek();
		if (ch == '[') {
			input.expect(ch);
			input.expect(']');
			return parseTypeTail(new ArrayTypeNode(input.getPlace(), base));
		} 
		if (ch == '<') {
			input.expect(ch);
			return parseTypeTail(new FunctionTypeNode(input.getPlace(), base, parseTypeList()));
		}
		
		return base;
	}
	

	/**
	 * Parses either a primitive type or a member type.
	 * 
	 * @return  the parsed base type
	 */
	public TypeNode parseBaseType() {
		char ch = input.skipAndPeek();
		boolean local = false;
		if (ch == '#') {
			input.expect('#');
			local = true;
		}
		String id = parseIdentifier();
		return new BasicTypeNode(input.getPlace(), local, nameCache.get(id));
	}
	
	
	/**
	 * Parses a type.
	 * 
	 * @return   the parsed type
	 */
	public TypeNode parseType() {
		TypeNode base = parseBaseType();
		return parseTypeTail(base);
	}
	
	
	protected BasicTypeName getName(String name) {
		return nameCache.get(name);
	}


}
